#pragma once

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <unordered_map>
#include <functional>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <memory>
#include <cassert>
#include <ctime>
#include <cstdarg>
#include <cstdlib>
#include <sys/stat.h>


namespace easylog
{

namespace util
{
    enum memory_size
    {
        m1M   = 1    * 1024 * 1024,
        m8M   = 8    * 1024 * 1024,
        m10M  = 10   * 1024 * 1024,
        m80M  = 80   * 1024 * 1024,
        m100M = 100  * 1024 * 1024,
        m512M = 512  * 1024 * 1024,
        m1G   = 1024 * 1024 * 1024,
    };

    struct date
    {
        static size_t now()
        {
            return (size_t)time(nullptr);
        }
    };

    struct file
    {
        static bool exist(const std::string& name)
        {
            struct stat s;    
            if (stat(name.c_str(), &s) < 0)
                return false;
            else
                return true;
        }

        static std::string dirpath(const std::string& name)
        {
            size_t pos = name.find_last_of("/\\"); // / \ 都可以
            if (pos == std::string::npos)
                return ".";
            else
                return name.substr(0, pos);
        }

        static void createdir(const std::string& name, size_t idx = 0)
        {
            umask(00);
            size_t pos = name.find_first_of("/\\", idx);

            if (pos == std::string::npos) // 创建一个目录
            {
                mkdir(name.c_str(), 0755);
            }
            else // 递归创建多个目录
            {
                std::string curdir = name.substr(0, pos);
                mkdir(curdir.c_str(), 0755);
      
                createdir(name, pos + 1); 
            }
        }
    };
}

struct log_level
{
    enum value
    {
        DEBUG = 0,
        INFO,
        WARN,
        ERROR,
        FATAL,
        OFF,
    };

    static const char* to_string(value level)
    {
        switch (level)
        {
        case value::DEBUG: return "DEBUG"; 
        case value::INFO:  return "INFO";  
        case value::WARN:  return "WARN";  
        case value::ERROR: return "ERROR"; 
        case value::FATAL: return "FATAL"; 
        case value::OFF:   return "OFF";   
        default:           return "UNKOWN";
        }
    }
};

struct log_msg
{
    time_t _generate_time;   // 日志的产生时间
    log_level::value _level; // 日志等级
    std::thread::id _tid;    // 线程ID
    std::string _file;       // 文件名
    size_t _line;            // 行号
    std::string _logger;     // 日志器名称
    std::string _payload;    // 消息主体
    
    log_msg(log_level::value level, const std::string& file, size_t line, const std::string& logger, const std::string& load)
        : _generate_time(util::date::now())
        , _level(level)
        , _tid(std::this_thread::get_id())
        , _file(file)
        , _line(line)
        , _logger(logger)
        , _payload(load)
    {}
};

// 抽象格式化子项基类
class format_item
{
public:
    using ptr = std::shared_ptr<format_item>;
    virtual void format(std::ostream& out, log_msg& msg) = 0;
};

// 格式化子项子类 -- 消息、等级、时间、文件名、行号、线程ID、日志器名、制表符、换行、其他
class payload_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << msg._payload; }
};

class level_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << log_level::to_string(msg._level); }
};

class time_format_item : public format_item
{
public:
    time_format_item(const std::string& fmt = "%F %H:%M:%S") : _time_fmt_str(fmt) 
    {}

    void format(std::ostream& out, log_msg& msg) override 
    {
        struct tm t; 
        localtime_r(&msg._generate_time, &t);

        char tmp[32] = {0};
        strftime(tmp, 32, _time_fmt_str.c_str(), &t);

        out << tmp;
    }
private:
    std::string _time_fmt_str; // %H:%M:%S
};

class file_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << msg._file; }
};

class line_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << msg._line; }
};

class thread_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << msg._tid; }
};

class logger_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << msg._logger; }
};

class tab_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << '\t'; }
};

class nline_format_item : public format_item
{
public:
    void format(std::ostream& out, log_msg& msg) override { out << '\n'; }
};

class other_format_item : public format_item
{
public:
    other_format_item(const std::string& s) : _s(s)
    {}

    void format(std::ostream& out, log_msg& msg) override { out << _s; }
private:
    std::string _s;
};

class formatter
{
public:
    using ptr = std::shared_ptr<formatter>;

public:
    formatter(const std::string& pattern = "[%d{%F %H:%M:%S}][%p][%t][%c][%f:%l]%T%m%n") 
        : _pattern(pattern)
    {
        assert(parse_pattern());
    }

    // 格式化msg
    void format(std::ostream& out, log_msg& msg)
    {
        for (auto& item : _items)
        {
            item->format(out, msg);
        }
    }

    std::string format(log_msg& msg)
    {
        std::stringstream ss;
        format(ss, msg);
        return ss.str();
    }

    // 解析格式化字符串 - 不是%就向后遍历，直到遇到%，前面是一个原始字符串子项，后面是一个控制字符
    bool parse_pattern()
    {
        size_t b = 0, e = 0;

        while (e < _pattern.size())
        {
            if (_pattern[e] == '%')
            {
                if (b != e) // 处理原始字符串
                {
                    std::string val(_pattern, b, e - b);
                    add_item("", val);

                    b = e;
                }

                if (e + 1 < _pattern.size())
                {
                    if (_pattern[e + 1] != '%') // 处理控制字符
                    {
                        if (e + 2 < _pattern.size() && _pattern[e + 2] == '{') // 处理子格式
                        {
                            e = _pattern.find('}');

                            if (e == std::string::npos)
                            {
                                return false;
                            }

                            std::string key(_pattern, b + 1, 1);
                            std::string val(_pattern, b + 3, e - (b + 3));
                            add_item(key, val);

                            ++e;
                            b = e;
                        }
                        else // 普通控制字符，没有子格式
                        {
                            std::string key(_pattern, e + 1, 1);
                            add_item(key, "");

                            e += 2;
                            b = e;
                        }
                    }
                    else // 双%
                    {
                        std::string val(_pattern, e + 1, 1);
                        add_item("", val);

                        e += 2;
                        b = e;
                    }
                }
                else // %后面没东西了
                {
                    return false;
                }
            }
            else
            {
                ++e;
            }
        }
        
        return true;
    }

private:
    // 根据格式控制字符创建格式化子项
    void add_item(const std::string& key, const std::string& val)
    {
        if      (key == "d")  _items.push_back(std::make_shared<time_format_item>(val));
        else if (key == "t")  _items.push_back(std::make_shared<thread_format_item>());
        else if (key == "c")  _items.push_back(std::make_shared<logger_format_item>());
        else if (key == "f")  _items.push_back(std::make_shared<file_format_item>());
        else if (key == "l")  _items.push_back(std::make_shared<line_format_item>());
        else if (key == "p")  _items.push_back(std::make_shared<level_format_item>());
        else if (key == "T")  _items.push_back(std::make_shared<tab_format_item>());
        else if (key == "m")  _items.push_back(std::make_shared<payload_format_item>());
        else if (key == "n")  _items.push_back(std::make_shared<nline_format_item>());
        else if (key.empty()) _items.push_back(std::make_shared<other_format_item>(val));
        else assert(false);
    }

private:
    std::string _pattern; // 格式化规则字符串
    std::vector<format_item::ptr> _items;
};



class log_sink
{
public:
    using ptr = std::shared_ptr<log_sink>;

public:
    log_sink()
    {}

    virtual ~log_sink()
    {}

    virtual void log(const std::string& data) = 0;
};

// 落地方向：标准输出
class stdout_sink : public log_sink
{
public:
    void log(const std::string& data) override { std::cout.write(data.c_str(), data.size()); }
};

// 落地方向：指定文件
class file_sink : public log_sink
{
public:
    file_sink(const std::string& path) : _file(path)
    {
        util::file::createdir(util::file::dirpath(path));

        _ofs.open(_file, std::ios::binary | std::ios::app);
        assert(_ofs.is_open());
    }

    void log(const std::string& data) override
    {
        _ofs.write(data.c_str(), data.size());
        assert(_ofs.good());
    }

private:
    std::string _file;
    std::ofstream _ofs;
};

// 落地方向：滚动文件（按大小滚动）
class rolling_sink : public log_sink
{
public:
    rolling_sink(const std::string& base_name, size_t max_size) 
        : _base_name(base_name), _max_size(max_size), _cur_size(0), _last_time(0)
    {
        util::file::createdir(util::file::dirpath(_base_name));
        rolling();
    }

    void log(const std::string& data) override
    {
        rolling();

        _ofs.write(data.c_str(), data.size());
        _cur_size += data.size();

        assert(_ofs.good());
    }

private:
    void rolling()
    {
        if (_cur_size == 0 || _cur_size >= _max_size)
        {
            time_t time = util::date::now();
            if (time == _last_time) // 防止一秒内重复滚动
                return;

            struct tm t;
            localtime_r(&time, &t);

            std::stringstream file_name;
            file_name << _base_name << '-' << (t.tm_year + 1900) << (t.tm_mon + 1) << t.tm_mday 
                      << t.tm_hour << t.tm_min << t.tm_sec << ".log";

            _ofs.close();
            _ofs.open(file_name.str(), std::ios::binary | std::ios::app);
            assert(_ofs.is_open());

            _cur_size = 0;
        }
    }

private:
    std::string _base_name; // 基础文件名 ./logs/<base>-20230803202848.log
    std::ofstream _ofs;

    size_t _max_size;
    size_t _cur_size;

    time_t _last_time; // 记录上一次滚动的时间
};

// 日志落地器工厂
class sink_factory
{
public:
    template<typename Sinker, typename... Args>
    static log_sink::ptr create(Args&&... args)
    {
        return std::make_shared<Sinker>(std::forward<Args>(args)...);
    }
};

class logger
{
public:
    using ptr = std::shared_ptr<logger>;

public:
    logger() {}

    // 多落地
    logger(const std::string& name, log_level::value level, formatter::ptr& fmtter, std::vector<log_sink::ptr>& sinkers)
        : _logger_name(name), _base_level(level), _formatter(fmtter), _sinkers(sinkers)
    {}

    void debug(const std::string& file, size_t line, const std::string fmt, ...)
    {
        if (_base_level > log_level::value::DEBUG)
            return;

        va_list ap;
        va_start(ap, fmt);

        //构建日志并落地
        log_msg msg(log_level::value::DEBUG, file, line, _logger_name, get_payload(fmt, ap));
        log(_formatter->format(msg));
    }

    void info(const std::string& file, size_t line, const std::string fmt, ...)
    {
        if (_base_level > log_level::value::INFO)
            return;

        va_list ap;
        va_start(ap, fmt);

        log_msg msg(log_level::value::INFO, file, line, _logger_name, get_payload(fmt, ap));
        log(_formatter->format(msg));
    }

    void warn(const std::string& file, size_t line, const std::string fmt, ...)
    {
        if (_base_level > log_level::value::WARN)
            return;

        va_list ap;
        va_start(ap, fmt);

        log_msg msg(log_level::value::WARN, file, line, _logger_name, get_payload(fmt, ap));
        log(_formatter->format(msg));
    }

    void error(const std::string& file, size_t line, const std::string fmt, ...)
    {
        if (_base_level > log_level::value::ERROR)
            return;

        va_list ap;
        va_start(ap, fmt);

        log_msg msg(log_level::value::ERROR, file, line, _logger_name, get_payload(fmt, ap));
        log(_formatter->format(msg));
    }

    void fatal(const std::string& file, size_t line, const std::string fmt, ...)
    {
        if (_base_level > log_level::value::FATAL)
            return;

        va_list ap;
        va_start(ap, fmt);

        log_msg msg(log_level::value::FATAL, file, line, _logger_name, get_payload(fmt, ap));
        log(_formatter->format(msg));
    }

    const std::string& name() { return _logger_name; }

protected:
    virtual void log(const std::string& data) = 0;

    std::string get_payload(const std::string& fmt, va_list& ap)
    {
        char* res;
        if (vasprintf(&res, fmt.c_str(), ap) == -1)
        {
            std::cout << "vasprintf failed!" << std::endl;
            return "vasprintf error";
        }
        va_end(ap);

        std::string ret(res);
        free(res);
        return ret;
    }

protected:    
    std::string _logger_name;

    std::atomic<log_level::value> _base_level;
    formatter::ptr _formatter;
    std::vector<log_sink::ptr> _sinkers;

    std::mutex _mutex;
};

class sync_logger : public logger
{
public:
    sync_logger(const std::string& name, log_level::value level, formatter::ptr& fmtter, std::vector<log_sink::ptr>& sinkers)
        : logger(name, level, fmtter, sinkers)
    {}

protected:
    virtual void log(const std::string& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);

        if (_sinkers.empty())
            return;

        for (auto& sinker : _sinkers)
        {
            sinker->log(data);
        }
    }
};

const size_t DEFAULT_BUFFER_SIZE = util::memory_size::m1M;
const size_t DEFAULT_THRESHOLD   = util::memory_size::m8M;
const size_t DEFAULT_INCREASE    = util::memory_size::m1M;

class buffer
{
public:
    buffer(size_t size = DEFAULT_BUFFER_SIZE)
        : _buffer(size), _writer_idx(0), _reader_idx(0)
    {}

    void push(const char* data, size_t len)
    {
        // if (len > writable_size())
        //     return;

        ensure_size(len); // 空间不够就扩容

        std::copy(data, data + len, &_buffer[_writer_idx]);
        move_writer(len);
    }

    const char* begin() 
    {
        return &_buffer[_reader_idx];
    }

    size_t readable_size() { return _writer_idx - _reader_idx; }

    size_t writable_size() { return _buffer.size() - _writer_idx; }

    void move_writer(size_t len) 
    {
        assert(len <= writable_size());
        _writer_idx += len; 
    }

    void move_reader(size_t len) 
    {
        assert(len <= readable_size());
        _reader_idx += len; 
    }

    void reset()
    {
        _reader_idx = 0;
        _writer_idx = 0;
    }

    void swap(buffer& b)
    {
        _buffer.swap(b._buffer);
        std::swap(_writer_idx, b._writer_idx);
        std::swap(_reader_idx, b._reader_idx);
    }

    bool empty() { return _writer_idx == _reader_idx; }

private:
    void ensure_size(size_t len)
    {
        if (len <= writable_size())
            return;

        size_t new_size = _buffer.size();

        do // 小于阈值翻倍增长，大于阈值增量增长
        { 
            if (_buffer.size() < DEFAULT_THRESHOLD)
                new_size *= 2;
            else
                new_size += DEFAULT_INCREASE; 
        }
        while (new_size < len);
            
        _buffer.resize(new_size);
    }

private:
    std::vector<char> _buffer;

    size_t _reader_idx;
    size_t _writer_idx;
};

enum looper_type
{
    SAFE_MODE,
    UNSAFE_MODE
};

class async_looper
{
public:
    using ptr = std::shared_ptr<async_looper>;
    using handler = std::function<void(buffer&)>;

public:
    async_looper(handler hd, 
            looper_type type = looper_type::SAFE_MODE) 
        : _callback(hd)
        , _stop(false)
        , _type(type)
        , _th(&async_looper::routine, this)
    {}

    ~async_looper() 
    { 
        stop(); 
    }

    void stop()
    {
        _stop = true;
        _csr_cv.notify_all();
        _th.join(); // 等待工作线程的退出，否则线程可能会出现中途退出，导致程序异常的情况
    }

    void push(const char* data, size_t len)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        if (_type == SAFE_MODE)
            _pdr_cv.wait(lock, [&](){ return _pdr_buf.writable_size() >= len; });

        _pdr_buf.push(data, len);
        _csr_cv.notify_one();
    }

    void push(const std::string& s) { push(s.c_str(), s.size()); }

private:
    void routine()
    {
        while (true)
        {
            // 只对交换过程加锁
            {
                std::unique_lock<std::mutex> lock(_mtx);

                if (_stop && _pdr_buf.empty()) // 应该是 &&
                    break;

                _csr_cv.wait(lock, [&](){ return !_pdr_buf.empty() || _stop; }); // 有数据或者退出前被唤醒
      
                _csr_buf.swap(_pdr_buf);

                if (_type == looper_type::SAFE_MODE)
                    _pdr_cv.notify_one();
            }

            _callback(_csr_buf);
            _csr_buf.reset();
        }
    }
    
    handler _callback; // 缓冲区的处理函数

private:
    std::atomic<bool> _stop;
    looper_type _type;

    buffer _pdr_buf; // producer buffer
    buffer _csr_buf; // consumer buffer

    std::condition_variable _pdr_cv;
    std::condition_variable _csr_cv;
    std::mutex _mtx;

    std::thread _th;
};


class async_logger : public logger
{
public:
    async_logger(const std::string& name, log_level::value level, 
            formatter::ptr& fmtter, 
            std::vector<log_sink::ptr>& sinkers,
            looper_type looper_type = looper_type::SAFE_MODE) 

        : logger(name, level, fmtter, sinkers)
        , _looper(std::make_shared<async_looper>
                (std::bind(&async_logger::looper_handler, this, std::placeholders::_1), looper_type))
    {}

protected:
    virtual void log(const std::string& data)
    {
        _looper->push(data);
    }

public:
    void looper_handler(buffer& bf)
    {
        if (_sinkers.empty())
            return;

        for (auto& sinker : _sinkers)
        {
            std::string data(bf.begin(), bf.readable_size());
            sinker->log(data);
        }
        bf.move_reader(bf.readable_size());
    }

private:
    async_looper::ptr _looper; // 异步工作器
};


/*
 * 日志器的建造者模式
 *
 * 1. 抽象一个建造者类 
 * 2. 派生出具体的建造者类
 *
 */

enum logger_type
{
    SYNC_LOGGER = 0,
    ASYNC_LOGGER,
};

class logger_builder
{
public:
    using ptr = std::shared_ptr<logger_builder>;

public:
    logger_builder()
        : _logger_type(logger_type::SYNC_LOGGER), _base_level(log_level::DEBUG), _looper_type(looper_type::SAFE_MODE)
    {}

    void build_logger_type(const logger_type type) { _logger_type = type; }
    void build_logger_name(const std::string name) { _logger_name = name; }
    void build_logger_level(log_level::value level) { _base_level = level; }
    void build_formatter(const std::string& pattern) { _formatter = std::make_shared<formatter>(pattern); }

    template<typename SinkType, typename... Args>
    void build_sinker(Args&&... args)
    {
        _sinkers.push_back(sink_factory::create<SinkType>(std::forward<Args>(args)...));
    }

    void build_looper_type(looper_type type = looper_type::SAFE_MODE) { _looper_type = type; }

    virtual logger::ptr build() = 0;

protected:
    logger_type _logger_type;
    std::string _logger_name;

    log_level::value _base_level;
    formatter::ptr _formatter;
    std::vector<log_sink::ptr> _sinkers;

    looper_type _looper_type;
};

// 局部日志器

class local_logger_builder : public logger_builder
{
public:
    logger::ptr build() override
    {
        assert(!_logger_name.empty());

        if (_formatter.get() == nullptr)
            _formatter = std::make_shared<formatter>();

        if (_sinkers.empty())
            build_sinker<stdout_sink>();

        if (_logger_type == logger_type::ASYNC_LOGGER)
            return std::make_shared<async_logger>(_logger_name, _base_level, _formatter, _sinkers, _looper_type);
        else
            return std::make_shared<sync_logger>(_logger_name, _base_level, _formatter, _sinkers);
    }
};

class logger_director
{
public:
    using ptr = std::shared_ptr<logger_director>;

public:
    logger_director(logger_builder::ptr builder)
        : _builder(builder)
    {}

    void construct(logger_type lg_type,
                   const std::string& name, 
                   log_level::value level,
                   const std::string& pattern = "[%d{%F %H:%M:%S}][%p][%t][%c][%f:%l]%T%m%n")
    {
        _builder->build_logger_type(lg_type);
        _builder->build_logger_name(name);
        _builder->build_logger_level(level);
        _builder->build_formatter(pattern);
        _builder->build_looper_type(looper_type::SAFE_MODE);
    }

    void enable_unsafe_mode()
    {
        _builder->build_looper_type(looper_type::UNSAFE_MODE);
    }

    template<typename SinkType, typename... Args>
    void construct_sinker(Args&&... args)
    {
        _builder->build_sinker<SinkType>(std::forward<Args>(args)...);
    }

private:
    logger_builder::ptr _builder;
};

/*
 * 日志器管理器类
 *
 */


class logger_manager
{
public:
    static logger_manager& get_instance()
    {
        static logger_manager eton; // c++11 static local variable's construct is thread safed
        return eton;
    }
    
    void add_logger(const logger::ptr& logger)
    {
        if (has_logger(logger->name()))
            return;

        std::unique_lock<std::mutex> lock(_mtx);

        _loggers.insert({logger->name(), logger});
    }

    bool has_logger(const std::string& name)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        if (_loggers.find(name) == _loggers.end())
            return false;
        else
            return true;
    }

    logger::ptr get_logger(const std::string& name)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        
        auto it = _loggers.find(name);
        if (it == _loggers.end()) 
            return nullptr;
        else 
            return it->second;
    }

    logger::ptr default_logger()
    {
        return _default_logger;
    }

private:
    logger_manager()
    {
        logger_builder::ptr lbuilder(new local_logger_builder);
        logger_director::ptr ldirector(new logger_director(lbuilder));

        ldirector->construct(logger_type::SYNC_LOGGER, "default", log_level::DEBUG);
        ldirector->construct_sinker<stdout_sink>();

        _default_logger = lbuilder->build();
        _loggers.insert({"default", _default_logger});
    }

private:
    std::mutex _mtx;
    logger::ptr _default_logger;
    std::unordered_map<std::string, logger::ptr> _loggers;
};

// 全局日志器
class global_logger_builder : public logger_builder
{
public:
    logger::ptr build() override
    {
        assert(!_logger_name.empty());

        if (_formatter.get() == nullptr)
            _formatter = std::make_shared<formatter>();

        if (_sinkers.empty())
            build_sinker<stdout_sink>();

        logger::ptr logger;

        if (_logger_type == logger_type::ASYNC_LOGGER)
            logger = std::make_shared<async_logger>(_logger_name, _base_level, _formatter, _sinkers, _looper_type);
        else
            logger = std::make_shared<sync_logger>(_logger_name, _base_level, _formatter, _sinkers);
        
        logger_manager::get_instance().add_logger(logger);
        return logger;
    }
};

logger::ptr get_logger(const std::string& name)
{
    return easylog::logger_manager::get_instance().get_logger(name);
}

logger::ptr default_logger()
{
    return easylog::logger_manager::get_instance().default_logger();
}

// 代理模式
#define debug(fmt, ...) debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define  info(fmt, ...)  info(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define  warn(fmt, ...)  warn(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define error(fmt, ...) error(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define fatal(fmt, ...) fatal(__FILE__, __LINE__, fmt, ##__VA_ARGS__)

}

#define DEBUG(fmt, ...) easylog::default_logger()->debug(fmt, ##__VA_ARGS__)
#define  INFO(fmt, ...) easylog::default_logger()-> info(fmt, ##__VA_ARGS__)
#define  WARN(fmt, ...) easylog::default_logger()-> warn(fmt, ##__VA_ARGS__)
#define ERROR(fmt, ...) easylog::default_logger()->error(fmt, ##__VA_ARGS__)
#define FATAL(fmt, ...) easylog::default_logger()->fatal(fmt, ##__VA_ARGS__)
